from profiles import *

class Node (object):
    def __init__(self, tag, text=None, parent=None,):
        self.tag = tag
        self.data = {}
        if text == '-LRB-':
            self.data['text'] = '('
        elif text == '-RRB-':
            self.data['text'] = ')'
        elif text == '-LSB-':
            self.data['text'] = '['
        elif text == '-RSB-':
            self.data['text'] = ']'
        else:
            self.data['text'] = text
        self.parent = parent
        self.children = []
        self.data['start'] = 0
        self.data['end'] = 0
        self.data['is_prot'] = False
        self.data['is_trig'] = False
        self.data['prot_names'] = ""
        self.data['trig_names'] = ""
        if text and self.data['text'].lower() in NEGATION_CUES:
            self.data['neg_cue'] = True
        else:
            self.data['neg_cue'] = False

    def set_text(self, text):
        if text == '-LRB-':
            self.data['text'] = '('
        elif text == '-RRB-':
            self.data['text'] = ')'
        elif text == '-LSB-':
            self.data['text'] = '['
        elif text == '-RSB-':
            self.data['text'] = ']'
        else:
            self.data['text'] = text
        if text and self.data['text'].lower() in NEGATION_CUES:
            self.data['neg_cue'] = True
        else:
            self.data['neg_cue'] = False
    
    def get_text(self):
        return self.data['text']
    
    def get_tag(self):
        return self.tag
    
    def set_start(self, start):
        self.data['start'] = start
    
    def set_end(self, end):
        self.data['end'] = end
        
    def set_is_prot(self, is_prot):
        self.data['is_prot'] = is_prot

    def set_is_trig(self, is_trig):
        self.data['is_trig'] = is_trig
    def add_child(self, child):
        self.children.append(child)
        child.parent = self
    
    def children(self):
        return self.children
    
    def parent(self):
        return self.parent

    def is_root(self):
        return not self.parent

    def get_root(self):
        if self.is_root():
            result = self
        else:
            result = self.parent.get_root()
        return result

    def is_leaf(self):
        return not self.children

    def items(self):
        result = [self]
        if not self.is_leaf():
            for child in self.children:
                result.extend(child.items())
        return result

    def leaves(self):
        result = []
        for i in self.items():
            if i.is_leaf():
                result.append(i)
        return result
    
    def __str__(self, indent = ' '):
        result = self.tag
        if not self.data == {}:
            result = result + ' ' + self.data.__str__() 
        if not self.is_leaf():
            for i in self.children:
                result = result + '\n' + indent + i.__str__(indent + ' ')

        return result

    def _path(self, node):
        '''
        Returns a list of nodes on the path from the object down to the given node.
        If no such path exists returns an empty list.
        >>> s2 = '(S (N (P (V w1))) (Q (R w2) (S w3)))'
        >>> r2 = make_tree(s2)

        '''
        result = []
        if self == node:
            result = [node]
        else:
            for child in self.children:
                x = child._path(node)
                if not x == []:
                    x.insert(0, self)
                    result = x
                    break
        
        return result
   
    def youngest_common_parent(self, node1, node2):
        if node1 == node2:
            return node1
        else:
            path1 = self._path(node1)
            path2 = self._path(node2)
            while(path1[0] == path2[0]):
                result = path1[0]
                path1 = path1[1:]
                path2 = path2[1:]
            
            return result

    def distance(self, node1, node2):
        compar = self.youngest_common_parent(node1, node2)
        result = len(compar._path(node1)) + len(compar._path(node2))
        return result

    def surface_distance(self, node1, node2):
        try:
            assert(node1 in self.leaves())
            assert(node2 in self.leaves())
            a = self.leaves().index(node1)
            b = self.leaves().index(node2)
            return a-b
        except:
            return MAX_DIST

    def group(self, criterion):
        # data[3] = is_trig in the form (True, 'T4')
        groupable = True
        leaves = self.leaves()
        '''
        The value of criterion would be:
        TRIGGER = 3
        PARTICIPANT = 2
        '''
        
        T_name = leaves[0].data[criterion]
        for leaf in leaves[1:]:
            if not leaf.data[criterion][0] or not leaf.data[criterion][1] == T_name:
                groupable = False
        if groupable:
            start = leaves[0].data['start']
            end = leaves[-1].data['end']
            if self.data == [] and criterion == 3:
                self.data.append(start, end, (False,), (True, T_name))
            elif self.data == [] and criterion == 2:
                self.data.append(start, end, (True, T_name), (False))
                
            
        


    def _lowestWithTag(self, node, tag = 'S'):
        '''
        Returns the closest parent to node with tag in the tree denoted by self.
        If no such node exists, returns None.
        '''
        result = None
        for n in self._path(node):
            if n.tag == tag:
                result = n
        return result

    def commands(self, node1, node2, tag = 'S'):
        l = self._lowestWithTag(node1, tag)
        if l and not l._path(node2) == []:
            return True
        else:
            return False





def make_tree(s):
    '''
    Takes the parsed sentence as a string and constructs a tree.
    Returns the root of the tree.
    >>> s2 = '(S (N (P (V w1))) (Q (R w2) (S w3)))'
    >>> r2 = make_tree(s2)
    >>> print r2
    S [None]
     N [None]
      P [None]
       V ['w1']
     Q [None]
      R ['w2']
      S ['w3']
    '''
    if "PARSE-FAILED" in s:
        return None
    else:
        tok = s.split()
        root = Node(tok[0].strip('()'))
        current = root
    
        for i in tok[1:]:
            token = i
            if token.startswith('('):
                new = Node(token.strip('()'))
                current.add_child(new)
                current = new
            else:               # it ends with ')' and it also is a word
                current.set_text(token.strip('()'))
                while token.endswith(')'):
                    # go one level up and remove a closing parenthesis off the tail of i
                    current = current.parent
                    token = token[:-1]
    
        return root
